<?php


namespace api\mobile;

use app\common\model\Mobile;
use think\facade\Db;

/**
 * 南京硅通[这家没跑了]
 * http://47.98.242.182:10186
 *
 * 话费下单地址：http://47.98.242.182:10186/plat/api/old/submitorder
 * 查询地址：http://47.98.242.182:10186/plat/api/old/queryorder
 * 查询余额地址：http://47.98.242.182:10186/plat/api/old/queryBalance
 *
 * 文档地址：https://docs.qq.com/doc/DWkV1VkxQVk13eEtQ
 * Class Guitong
 * @package api
 */
class Guitong extends BaseModel
{
    private $szFormat = 'JSON';  // XML 或 JSON
    private $header = [
        'Content-Type: application/json',
    ];

    // 接口状态码映射表
    private $errorMap = [
        // 充值
        1000 => '系统接口维护',
        1001 => '参数错误',
        1003 => 'szTimeStamp时间参数与我方收到订单的时间晚于1分钟以上，或者初始化卡单上限',
        1004 => '签名错误',
        2001 => '号段错误',
        2002 => '运营商错误',
        2003 => '号码黑名单',
        2020 => 'IP白名单限制',
        2021 => '用户不存在',
        1006 => '余额不足',
        2030 => '产品没有配置或关闭',
        3003 => '订单号重复',
        2050 => '下单异常',

        // 查询
        5001 => '查询接口关闭',
        5002 => '查询频繁',
        5003 => '参数有误',
        5004 => '签名错误',
        5011 => '充值中',
        5012 => '充值成功',
        5013 => '充值失败',
        5019 => '对账状态',
        5005 => '订单不存在',

        // 余额查询
        1 => '请求失败',

        999 => '系统异常',
    ];

    /**
     * 创建订单
     * /plat/api/old/submitorder
     * @param $order
     * @return bool|mixed|string
     */
    public function create($order)
    {
        // 除了充值 50 100 200 的面值外，不符合的接口将会提示 nRtn=2030 没有该产品

        $url = $this->info['url'] . '/plat/api/old/submitorder?';
        $sortType = getMobileIsp($order['mobile']);
        $sortType = $sortType == -1 ? 1 : $sortType;  // 1 移动
        $params = [
            'szAgentId' => $this->info['app_id'],
            'szOrderId' => $order['sub_order_id'],   // 商户平台自行生成的订单编号
            'szPhoneNum' => $order['mobile'],  // 充值号码、QQ号、加油卡号
            'nMoney' => fen2yuan($order['amount']),  // 充值金额
            'nSortType' => $sortType,  // 运营商编码
            'nProductClass' => 1,  // 固定值
            'nProductType' => 1,  // 固定值
            'szProductId' => '',
            'szTimeStamp' => date('Y-m-d H:i:s'),
        ];

        $params['szVerifyString'] = $this->getSign($params);
        $params['szFormat'] = $this->szFormat;
        $params['szNotifyUrl'] =  $this->notifyUrl; // 完成结果回调通知地址（不参与签名）
        $url .= http_build_query($params);
        $res = curlPost($url, json_encode($params), $this->header, 5, true);
        $res = json_decode($res, true);

        $supplier_order_no = '';
        $returnCode = $res['nRtn'] ?? 2050;
        $remark = $this->errorMap[$returnCode] ?? '下单失败';
        switch ($returnCode) {
            case 0: // 提交成功	下单成功
            case 3003: // 订单号重复	等待回调或者查询结果
                $status = self::SUCCESS;
                break;
            case 1000: // 系统接口维护	下单失败
            case 1001: // 参数错误	下单失败
            case 1003: // szTimeStamp时间参数与我方收到订单的时间晚于1分钟以上，或者初始化卡单上限 下单失败
            case 1004: // 签名错误	下单失败
            case 2001: // 号段错误	下单失败
            case 2002: // 运营商错误	下单失败
            case 2003: // 号码黑名单	下单失败
            case 2020: // IP白名单限制	下单失败
            case 2021: // 用户不存在	下单失败
            case 1006: // 余额不足	下单失败
            case 2030: // 产品没有配置或关闭	下单失败
                $status = self::FAIL;
                break;
            case 2050: // 下单异常	未确认（人工核实）或者通过查询接口确认
            case 999: // 系统异常	未确认（人工核实）或者通过查询接口确认； 面值必须传整数
            default: //  其它，或者网络异常		未确认（人工核实）或者通过查询接口确认
                $status = self::NEED_CONFIRM;
                break;
        }

        return compact('status', 'supplier_order_no', 'remark');
    }

    /**
     * 订单查询
     * @param $orderId
     * @return bool|mixed|string
     */
    public function query($orderId)
    {
        $url = $this->info['url'] . '/plat/api/old/queryorder';
        $params = [
            'szAgentId' => $this->info['app_id'],
            'szOrderId' => $orderId,
        ];
        $params['szVerifyString'] = $this->getSign($params);
        $params['szFormat'] = $this->szFormat;
        $res = curlPost($url, json_encode($params), $this->header);
        $res = json_decode($res, true);
        return $res;
    }

    /**
     * 查询余额
     * /plat/api/old/queryBalance
     * @return bool|int
     */
    public function queryAmount()
    {
        $url = $this->info['url'] . '/plat/api/old/queryBalance?';
        $params = [
            'szAgentId' => $this->info['app_id'],
            'szVerifyString' => $this->getSign(),
            'szFormat' => $this->szFormat,
        ];

        $url .= http_build_query($params);
        $res = curlGet($url, $this->header);
        $res = json_decode($res, true);
        if ($res['nRtn'] === 0) {
            return yuan2fen($res['fBalance']);
        }

        $this->error = $this->errorMap[$res['nRtn']] ?? '查询错误';

        return false;
    }

    /**
     * 订单回调
     * @param $params
     * @return string
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function notify($params)
    {
        $subOrderId = $params['szOrderId'] ?? '';
        $szRtnMsg = $params['szRtnMsg'] ?? ''; // 官方流水 => 凭证
        $sign = $params['szVerifyString'] ?? '';
        $retText = 'ok';

        if (isset($params['szRtnMsg'])) {
            unset($params['szRtnMsg']);
        }

        $checkSign = $this->getSign([
            'szAgentId' => $params['szAgentId'],
            'szOrderId' => $params['szOrderId'],
            'szPhoneNum' => $params['szPhoneNum'],
            'nDemo' => $params['nDemo'],
            'fSalePrice' => $params['fSalePrice'],
            'nFlag' => $params['nFlag'],
        ]);
        if ($sign !== $checkSign) {
            $insert['result'] = '验签失败';
            Db::name('mobile_notify')->insert($insert);

            return $insert['result'];
        }

        if (!isset($params['nFlag']) || !in_array($params['nFlag'], [2, 3])) {
            // 接口状态不存在时，不处理
            $insert['result'] = '接口状态未知,不处理';
            Db::name('mobile_notify')->insert($insert);

            return $insert['result'];
        }

        $insert = [
            'sub_order_id' => $subOrderId,
            'params' => json_encode($params, JSON_UNESCAPED_UNICODE),
            'result' => $retText,
        ];

        $order = Db::name('mobile_order')
            ->where('sub_order_id', $subOrderId)
            ->where('mobile_supplier_id', $this->info['id'])
            ->find();
        if (empty($order)) {
            $insert['result'] = '订单不存在';
            Db::name('mobile_notify')->insert($insert);

            return $insert['result'];
        }

        // 判断面额是否一致
        if ($params['nDemo'] != fen2yuan($order['amount'])) {
            $insert['result'] = '面额有误' . $params['nDemo'];
            Db::name('mobile_notify')->insert($insert);

            return $insert['result'];
        }

        if (in_array($order['supplier_status'], [3, 4, 99])) {
            $insert['result'] = '已经成功过';
            Db::name('mobile_notify')->insert($insert);

            return $retText;
        }

        if ($order['supplier_status'] != 2) {
            $insert['result'] = '订单状态有误：' . $order['supplier_status'];
            Db::name('mobile_notify')->insert($insert);

            return $insert['result'];
        }

        // 供应商订单状态：1订单未创建 2订单已创建 3充值成功 4充值失败 99充值失败，等待重试
        // nFlag 接口状态: 2 成功 3 失败
        $supplierStatusMap = [
            2 => 3,
            3 => 99,
        ];

        $supplierStatus = $supplierStatusMap[$params['nFlag']];
        if ($order['supplier_status'] == $supplierStatus) {
            Db::name('mobile_notify')->insert($insert);

            return $retText;
        }
        $update = [
            'official_sn' => $szRtnMsg,
            'supplier_status' => $supplierStatus,
        ];
        if (in_array($supplierStatus, [3, 99])) {
            $update['finish_time'] = time();
        }
        if ($supplierStatus == 99) {
            $order['notify_url'] = '';  // 失败通知先不发
        }

        Db::name('mobile_notify')->insert($insert);

        Db::startTrans();
        try {
            // 如果是返佣模式，将订单结算方式改成待结算
            if ($supplierStatus == 3 && $order['settle_type'] == SETTLE_TYPE_PROFIT) {
                if ($order['profit'] == 0) {
                    $update['settle_status'] = 3;
                } elseif ($order['settle_status'] == 1) {
                    $update['settle_status'] = 2;
                }
            }

            Mobile::handleNotify($order, $update);

            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();

            $insert['result'] = '订单更新失败' . $order['id'] . '。' . $e->getMessage();
            Db::name('mobile_notify')->insert($insert);

            return $insert['result'];
        }

        return $retText;
    }

    /**
     * 生成签名
     * @param $params
     * @return string
     */
    public function getSign($params = [])
    {
        if ($params) {
            if (isset($params['szVerifyString'])) {
                unset($params['szVerifyString']);
            }
            $str = '';
            foreach ($params as $k => $v) {
                if ($v !== '') {
                    $str .= $k . '=' . $v . '&';
                }
            }
            $str .= 'szKey=' . $this->info['app_secret'];
        } else {
            $str = 'szAgentId=' . $this->info['app_id'] . '&szKey=' . $this->info['app_secret'];
        }
        return strtolower(md5($str));
    }
}
